using System.Collections;
using System.Collections.Generic;
using System.Data.Linq;
using System.Linq;
using Magiq.Delete;
using Magiq.Insert;
using Magiq.Support;
using Magiq.Update;

namespace Magiq.Sql {
    public class MagiqToSql : IMagiqProvider {
        private const string DataQueryAssemblyQualifiedName =
            "System.Data.Linq.DataQuery`1, System.Data.Linq, Version=3.5.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089";

        private readonly MagiqToSqlQueryableFactory queryableFactory = new MagiqToSqlQueryableFactory();

        public MagiqToSql() {
            DependentTablesUpdateStrategy = new NotSupportedDependentTablesUpdateStrategy();
        }

        public IDependentTablesUpdateStrategy DependentTablesUpdateStrategy { get; set; }

        #region IMagiqProvider Members

        public IUpdateProvider GetUpdateProvider() {
            return new MagiqToSqlUpdateProvider(DependentTablesUpdateStrategy);
        }

        public IDeleteProvider GetDeleteProvider() {
            return new MagiqToSqlDeleteProvider();
        }

        public IInsertProvider GetInsertProvider() {
            return new MagiqToSqlInsertProvider();
        }

        public IQueryable<T> CreateQueryableFrom<T>(IEnumerable<T> enumerable) where T : class {
            var entitySet = enumerable as EntitySet<T>;

            if (entitySet != null && entitySet.IsDeferred)
                return queryableFactory.GetFrom((EntitySet<T>) enumerable);

            return enumerable.AsQueryable();
        }

        public bool Handles(IEnumerable source) {
            var type = source.GetType();
            if (!type.IsGenericType)
                return false;

            var genericType = type.GetGenericTypeDefinition();
            return genericType.AssemblyQualifiedName == DataQueryAssemblyQualifiedName ||
                   genericType == typeof (Table<>) || genericType == typeof (EntitySet<>);
        }

        public bool Handles(IEnumerable source, IEnumerable destination) {
            if (! Handles(source))
                return false;

            var type = destination.GetType();
            if (!type.IsGenericType)
                return false;

            var genericType = type.GetGenericTypeDefinition();
            return genericType == typeof(MagiqToSqlQueryable<>) || 
                    genericType == typeof(Table<>) ||
                   (genericType == typeof (EntitySet<>) && destination.Property<bool>("IsDeferred"));
        }

        public IItemsAdder<T> ItemAdderFor<T, S>(IEnumerable<S> enumerable) where S : class {
            if (enumerable is Table<S>)
                return new TableAdder<T,S>((Table<S>)enumerable);

            if (enumerable is EntitySet<S>)
                return new ListItemsAdder<T>((EntitySet<S>)enumerable);
            return null;
        }

        #endregion
    }
}